package com.tyijian.admin.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.collections.DefaultRedisList;
import org.springframework.data.redis.support.collections.RedisList;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.tyijian.admin.core.exceptionHandle.RestException;
import com.tyijian.admin.entity.SysRole;
import com.tyijian.admin.entity.SysUser;
import com.tyijian.admin.repository.jpa.UserDao;
import com.tyijian.springside.core.Constant;
import com.tyijian.springside.core.StatusConstant;
import com.tyijian.springside.core.persistence.DynamicSpecifications;
import com.tyijian.springside.core.persistence.SearchFilter;
import com.tyijian.springside.core.utils.Clock;
import com.tyijian.springside.core.utils.Digests;
import com.tyijian.springside.core.utils.Encodes;

@Component
@Transactional
public class UserService {

	@Autowired
	private UserDao userDao;

	@Autowired
	private RedisTemplate<String, SysUser> redisTemplate;

	private Clock clock = Clock.DEFAULT;

	/**
	 * 根据用户名查找用户
	 * 
	 * @param name
	 * @return
	 */
	@Cacheable(key = "'loginname:'+#name", value = "SysUser")
	public SysUser findByLoginname(String name) {
		return userDao.findByLoginname(name);
	}

	/**
	 * 根据ID查找用户
	 * 
	 * @param id
	 * @return
	 */
	@Cacheable(key = "'id:'+#id", value = "SysUser")
	public SysUser get(Long id) {
		return userDao.findOne(id);
	}

	/**
	 * 验证登录密是否正确
	 * 
	 * @param user
	 *            登录的用户对象
	 * @param password
	 *            登录密码
	 * @return
	 */
	public boolean passwordsMatch(SysUser user, String password) {
		byte[] salt = Encodes.decodeHex(user.getSalt());
		byte[] hashPassword = Digests.sha1(password.getBytes(), salt, Constant.HASH_INTERATIONS);
		return user.getPassword().equals(Encodes.encodeHex(hashPassword));
	}

	/**
	 * 查看所有用户,先从RedisList缓存里取数据，没有的就从数据库里面获取
	 * 
	 * @return
	 */
	public List<SysUser> findAll() {
		DefaultRedisList<SysUser> users = (DefaultRedisList<SysUser>) getUserRedisList("SysUserList");
		SysUser[] userArray = users.toArray(new SysUser[users.size()]);
		List<SysUser> list = new ArrayList<SysUser>(Arrays.asList(userArray));

		if (list == null || list.size() == 0) {
			list = (List<SysUser>) userDao.findAll();
			users.addAll(list);
		}
		return list;
	}

	/**
	 * Internal method for getting a list from Redis DB 缓存失效时间为2小时
	 */
	private RedisList<SysUser> getUserRedisList(String key) {
		redisTemplate.expire(key, 60 * 60 * 2L, TimeUnit.SECONDS);
		DefaultRedisList<SysUser> users = new DefaultRedisList<SysUser>(key, redisTemplate);
		return users;
	}

	/**
	 * 缓存token
	 * 
	 * @param key
	 * @param token
	 * @param expireTime
	 *            失效时间
	 */
	public void setTokenCache(String key, SysUser user, Long expireTime) {
		// 放到redis 发布/订阅功能
		redisTemplate.convertAndSend("loginName", user.getLoginname());
		redisTemplate.opsForValue().set(key, user, expireTime, TimeUnit.SECONDS);
	}

	/**
	 * 获取缓存中的token
	 * 
	 * @param key
	 * @return
	 */
	public SysUser getUserFromCache(String key) {
		return (SysUser) redisTemplate.opsForValue().get(key);
	}

	/**
	 * 保存用户
	 * 
	 * @param user
	 */
	@Caching(put = { @CachePut(key = "'loginname:'+#user.loginname", value = "SysUser") })
	public void save(SysUser user) {

		redisTemplate.opsForValue().set("123", user);

		entryptPassword(user);
		user.setStatus(StatusConstant.USER_STATUS);
		user.setCreatetime(clock.getCurrentDate());
		userDao.save(user);
	}

	/**
	 * 删除用户
	 * 
	 * @param user
	 */
	@Caching(evict = { @CacheEvict(key = "'loginname:'+#user.loginname", value = "SysUser"),
			@CacheEvict(key = "'id:'+#user.id", value = "SysUser") })
	public void delete(SysUser user) {
		if (isSupervisor(user.getId())) {
			throw new RestException("不能删除超级管理员用户");
		}
		userDao.deleteUserRole(user.getId());
		userDao.delete(user);
	}

	/**
	 * 设定安全的密码，生成随机的salt并经过1024次 sha-1 hash
	 * 
	 * @param user
	 */
	private void entryptPassword(SysUser user) {
		byte[] salt = Digests.generateSalt(Constant.SALT_SIZE);
		user.setSalt(Encodes.encodeHex(salt));

		byte[] hashPassword = Digests.sha1(user.getPassword().getBytes(), salt, Constant.HASH_INTERATIONS);
		user.setPassword(Encodes.encodeHex(hashPassword));
	}

	/**
	 * 
	 * 得到分页User 对象
	 * 
	 * @param userId
	 * @param searchParams
	 * @param pageNumber
	 * @param pageSize
	 * @param sortType
	 * @return
	 */
	public Page<SysUser> getUserPageList(Map<String, Object> searchParams, int pageNumber, int pageSize, String sortType) {
		PageRequest pageRequest = buildPageRequest(pageNumber, pageSize, sortType);
		Specification<SysUser> spec = buildSpecification(searchParams);

		return userDao.findAll(spec, pageRequest);
	}

	/**
	 * 创建分页请求.
	 */
	private PageRequest buildPageRequest(int pageNumber, int pageSize, String sortType) {
		Sort sort = null;
		if ("auto".equals(sortType)) {
			sort = new Sort(Direction.DESC, "id");
		} else if ("name".equals(sortType)) {
			sort = new Sort(Direction.ASC, "name");
		}

		return new PageRequest(pageNumber - 1, pageSize, sort);
	}

	/**
	 * 创建动态查询条件组合.
	 */
	private Specification<SysUser> buildSpecification(Map<String, Object> searchParams) {
		Map<String, SearchFilter> filters = SearchFilter.parse(searchParams);
		Specification<SysUser> spec = DynamicSpecifications.bySearchFilter(filters.values(), SysUser.class);
		return spec;
	}

	/**
	 * 获取用户的角色列表
	 * 
	 * @param user
	 *            用户对象
	 * @return
	 */
	@Cacheable(key = "'roles:'+#user.loginname", value = "SysUser")
	public List<SysRole> getRoleList(SysUser user) {
		Long[] ids = userDao.findSysUserRoleIds(user.getId());
		return userDao.getRoleListByUserId(ids);
	}
	
	/**
	 * 更新用户
	 * 
	 * @param user
	 */
	public void updateUser(SysUser user) {
		if (StringUtils.isNotBlank(user.getPassword())) {
			entryptPassword(user);
		}
		userDao.save(user);
	}
	
	/**
	 * 判断是否超级管理员.
	 */
	private boolean isSupervisor(Long id) {
		return id == 1;
	}


	/**
	 * 保存用户的角色
	 * 
	 * @param userId
	 * @param roleId
	 */
	public void saveUserRole(SysUser user) {
		userDao.save(user);
	}

}